//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#define MINTIMEINTERVAL0 10/*ms*/
#define MINTIMEINTERVAL1 1/*s/min/h*/
#define TIMER_PER_CHANNEL 10

typedef enum {
    TIMER_STOP      = 0,
    TIMER_START     = 1,
}TIMERSTATE;

typedef struct MTimerInfo{
    int         nHandle;
    TIMERSTATE  State;
    UInt32       nInterval;
    UInt32       nCur;
    TimerCallBackProc      pCallBkProc;
    void *      pvArg;
    UInt32       EventID;
    UInt32       Resolution;
}MTimerInfo;

static void OnTimerProc1(int eType, int nCode, void *pParam);
static void OnTimerProc2(int eType, int nCode, void *pParam);
static void OnTimerProc3(int eType, int nCode, void *pParam);

class CMultiTimers : public CDeviceDriver {
public:
    CMultiTimers();

    ~CMultiTimers();

    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf &buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf &inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose() {}

public:
    void OnTimer0(int nCode, void *pParam);
    void OnTimer1(int nCode, void *pParam);
    void OnTimer2(int nCode, void *pParam);
    void OnTimer3(int nCode, void *pParam);
    ECode InitTimer();

private:

    void TryStopLowerTimer(int Resolution);
    void TryStartLowerTimer(int Resolution);
    ECode StopTimer0();
    void StopTimer1();
    void StopTimer2();
    void StopTimer3();
    ECode StartTimer0();
    void StartTimer1();
    void StartTimer2();
    void StartTimer3();

public:
    MTimerInfo MTimers[4*TIMER_PER_CHANNEL+3];// array for all timers
    IDeviceDriver *m_pTimer;
    bool UserQueueCreated;
    bool PowerQueueCreated;
    DzTimer  * m_pMultiChannel1;
};

void ChannelRoutine1 (void *pvParameter);

CMultiTimers::CMultiTimers()
{
    int i;
    for(i = 0; i < 4*TIMER_PER_CHANNEL+3; i++) {
        MTimers[i].nHandle = -1;
    }
    m_pTimer = NULL;
    UserQueueCreated=false;
    PowerQueueCreated=false;

    return ;
}

CMultiTimers::~CMultiTimers()
{
}

ECode CMultiTimers::Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CMultiTimers::Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf &buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

void EnQueueProc(int eType, int nCode, void *pParam);
void QueueProc(int eType, int nCode, void *pParam);
int GetTimestamp();
ECode CMultiTimers::Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf &inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec = E_ACCESS_DENIED;
    switch(nControlCode) {
        case 11: {//Create
            TimerInfo * pInfo=(TimerInfo *)inBuffer.GetPayload();
            int i=pInfo->Resolution*TIMER_PER_CHANNEL;
            for(int j=0; j<TIMER_PER_CHANNEL; j++,i++) {//
                MTimerInfo *pMTimer = &MTimers[i];
                if (-1 == pMTimer->nHandle) {
                    int TimerID = i;
                    pMTimer->nHandle = TimerID;
                    pMTimer->State = TIMER_STOP;
                    pMTimer->nInterval = pInfo->Interval;
                    pMTimer->nCur = 0;
                    pMTimer->pCallBkProc=pInfo->pCallbackProc;
                    pMTimer->pvArg=pInfo->pvArg;
                    pMTimer->Resolution=pInfo->Resolution;
                    pOutBuffer->Copy((Byte *)&TimerID, sizeof(int));
                    if(pInfo->eType==EVENT_USER) {//CreateTimer
                        cprintf("user timer id %d\n",TimerID);
                        if(UserQueueCreated){
                            pMTimer->EventID = pInfo->eType;
                            AddNotify(EVENT_USER,(void *)QueueProc);
                        }
                        else{
                            QueueInfo Info = {EVENT_USER, 8, 10};
                            MemoryBuf ebbIn((Byte *)&Info, sizeof(QueueInfo), sizeof(QueueInfo));
                            ec = g_pEventQueues->Control(1, ebbIn, NULL, NULL);
                            assert(!FAILED(ec));
                            pMTimer->EventID = pInfo->eType;
                            AddNotify(EVENT_USER,(void *)QueueProc);
                            UserQueueCreated=true;
                        }
                    }
                    else if(pInfo->eType==EVENT_POWER) {//internal use
                        if(PowerQueueCreated){
                            pMTimer->EventID = pInfo->eType;
                            AddNotify(EVENT_POWER,(void *)QueueProc);
                        }
                        else{
                            QueueInfo Info = {EVENT_POWER, 8, 16};
                            MemoryBuf ebbIn((Byte *)&Info, sizeof(QueueInfo), sizeof(QueueInfo));
                            ec = g_pEventQueues->Control(1, ebbIn, NULL, NULL);
                            assert(!FAILED(ec));
                            pMTimer->EventID = pInfo->eType;
                            AddNotify(EVENT_POWER,(void *)QueueProc);
                            PowerQueueCreated=true;
                        }
                        //cprintf("TimerID is %d,Resolution %d,interval %d,handle %d\n",i,pInfo->Resolution,pInfo->Interval,MTimers[i].nHandle);
                    }
                    else {//CreateQueuedTimer
                        QueueInfo Info = {EVENT_TIMER+TimerID, 4, 16};
                        MemoryBuf ebbIn((Byte *)&Info, sizeof(QueueInfo), sizeof(QueueInfo));
                        ec = g_pEventQueues->Control(1, ebbIn, NULL, NULL);
                        assert(!FAILED(ec));
                        pMTimer->EventID = EVENT_TIMER+TimerID;
                        AddNotify(EVENT_TIMER+TimerID,(void *)QueueProc);
                    }
                    ec=NOERROR;
                    break;
                }
            }
            break;
        }
        case 12: {//Kill
            int TimerID = *(int *)inBuffer.GetPayload();
            MTimerInfo *pMTimer = &MTimers[TimerID];
            if (pMTimer->nHandle == TimerID) {
                pMTimer->State = TIMER_STOP;
                pMTimer->nHandle = -1;
                TryStopLowerTimer(pMTimer->Resolution);
                ec = NOERROR;
            }
            break;
        }
        case 13: {//SetInterval
            int *pParam = (int *)inBuffer.GetPayload();
            int TimerID = pParam[0];
            UInt32 Interval = (UInt32)pParam[1];
            MTimerInfo *pMTimer = &MTimers[TimerID];
            TIMERSTATE SaveState = pMTimer->State;
            pMTimer->State = TIMER_STOP;
            pMTimer->nInterval = Interval;
            pMTimer->nCur = 0;
            pMTimer->State = SaveState;
            ec = NOERROR;
            break;
        }
        case 14: {//Start
            int TimerID = *(int *)inBuffer.GetPayload();
            MTimerInfo *pMTimer = &MTimers[TimerID];
            if (pMTimer->State == TIMER_STOP) {
                pMTimer->nCur = 0;
                TryStartLowerTimer(pMTimer->Resolution);
                pMTimer->State = TIMER_START;
                ec = NOERROR;
            }
            break;
        }
        case 15: {//Stop
            int TimerID = *(int *)inBuffer.GetPayload();
            MTimerInfo *pMTimer = &MTimers[TimerID];
            if (pMTimer->State == TIMER_START) {
                pMTimer->State = TIMER_STOP;
                TryStopLowerTimer(pMTimer->Resolution);
                ec = NOERROR;
            }
            break;
        }
        case 16: {//Restart
            int *pParam = (int *)inBuffer.GetPayload();
            int TimerID = pParam[0];
            UInt32 Interval = (UInt32)pParam[1];
            MTimerInfo *pMTimer = &MTimers[TimerID];
            pMTimer->nCur = 0;
            pMTimer->nInterval = Interval;
            TryStartLowerTimer(pMTimer->Resolution);
            pMTimer->State = TIMER_START;
            ec = NOERROR;
            break;
        }
        case 17: {//Short
            int TimerID = *(int *)inBuffer.GetPayload();
            MTimerInfo *pMTimer = &MTimers[TimerID];
            if (pMTimer->State == TIMER_START) {
                pMTimer->nCur = 0;
                NotifyByIsr(pMTimer->EventID,(int)pMTimer->pCallBkProc,pMTimer->pvArg);
                ec = NOERROR;
            }
            break;
        }
        case 18: {//GetLeftTime
            assert(pOutBuffer && (pOutBuffer->GetCapacity() >= (int)sizeof(int)));
            int TimerID = *(int *)inBuffer.GetPayload();
            MTimerInfo *pMTimer = &MTimers[TimerID];
            if (pMTimer->State == TIMER_START) {
                int t = pMTimer->nInterval - pMTimer->nCur;
                pOutBuffer->Copy((Byte *)&t, sizeof(int));
            }
            else {
                int t = -1;
                pOutBuffer->Copy((Byte *)&t, sizeof(int));
            }
            ec = NOERROR;
            break;
        }
        case 20: {//OSTGetTicks //ms unit
            assert(pOutBuffer && (pOutBuffer->GetCapacity() >= (int)sizeof(int)));
            int Ticks;
            Ticks = GetTimestamp();
            pOutBuffer->Copy((Byte *)&Ticks, sizeof(int));
            ec = NOERROR;
            break;
        }
        default:
            break;
    }
    return ec;
}

//Dispatch OnTimer
void CMultiTimers::OnTimer0(int nCode, void *pParam)
{
    for(int i=0;i<TIMER_PER_CHANNEL;i++) {
        if (MTimers[i].State == TIMER_START) {
            MTimers[i].nCur += MINTIMEINTERVAL0;
            if (MTimers[i].nCur >= MTimers[i].nInterval) {
                NotifyByIsr(MTimers[i].EventID,(int)MTimers[i].pCallBkProc,MTimers[i].pvArg);
                MTimers[i].nCur = 0;
            }
        }
    }
}

void CMultiTimers::OnTimer1(int nCode, void *pParam)
{
    for(int i=TIMER_PER_CHANNEL;i<TIMER_PER_CHANNEL*2;i++) {
            if (MTimers[i].State == TIMER_START) {
                MTimers[i].nCur += MINTIMEINTERVAL1;
            if (MTimers[i].nCur >= MTimers[i].nInterval) {
                MTimers[i].nCur = 0;
                NotifyByIsr(MTimers[i].EventID,(int)MTimers[i].pCallBkProc,MTimers[i].pvArg);

            }
        }
    }
}

void CMultiTimers::OnTimer2(int nCode, void *pParam)
{
    for(int i=TIMER_PER_CHANNEL*2;i<TIMER_PER_CHANNEL*3;i++) {
        if (MTimers[i].State == TIMER_START) {
            MTimers[i].nCur += MINTIMEINTERVAL1;
            if (MTimers[i].nCur >= MTimers[i].nInterval) {
                NotifyByIsr(MTimers[i].EventID,(int)MTimers[i].pCallBkProc,MTimers[i].pvArg);
                MTimers[i].nCur = 0;
            }
        }
    }
}

void CMultiTimers::OnTimer3(int nCode, void *pParam)
{
    for(int i=TIMER_PER_CHANNEL*3;i<TIMER_PER_CHANNEL*4;i++) {
        if (MTimers[i].State == TIMER_START) {
            MTimers[i].nCur += MINTIMEINTERVAL1;
            if (MTimers[i].nCur >= MTimers[i].nInterval) {
                NotifyByIsr(MTimers[i].EventID,(int)MTimers[i].pCallBkProc,MTimers[i].pvArg);
                MTimers[i].nCur = 0;
            }
        }
    }
    return ;
}

void CMultiTimers::TryStopLowerTimer(int Resolution)
{
    switch(Resolution)
    {
        case 0: {
            for(int i=0;i<TIMER_PER_CHANNEL;i++)
                if(MTimers[i].State == TIMER_START)return;
            StopTimer0();
            break;
        }
        case 1: {
            for(int i=TIMER_PER_CHANNEL;i<2*TIMER_PER_CHANNEL;i++)
                if(MTimers[i].State == TIMER_START)return;
            StopTimer1();
            break;
        }
        case 2: {
            for(int i=2*TIMER_PER_CHANNEL;i<3*TIMER_PER_CHANNEL;i++)
                if(MTimers[i].State == TIMER_START)return;
            StopTimer2();
            break;
        }
        case 3: {
            for(int i=3*TIMER_PER_CHANNEL;i<4*TIMER_PER_CHANNEL;i++)
                if(MTimers[i].State == TIMER_START)return;
            StopTimer3();
            break;
        }
        default :break;
    }
    return;
}

void CMultiTimers::TryStartLowerTimer(int Resolution)
{
    switch(Resolution)
    {
        case 0: {
            for(int i=0;i<TIMER_PER_CHANNEL;i++)
                if(MTimers[i].State == TIMER_START)return;
            StartTimer0();
            break;
        }
        case 1: {
            for(int i=TIMER_PER_CHANNEL;i<2*TIMER_PER_CHANNEL;i++)
                if(MTimers[i].State == TIMER_START)return;
            StartTimer1();
            break;
        }
        case 2: {
            for(int i=2*TIMER_PER_CHANNEL;i<3*TIMER_PER_CHANNEL;i++)
                if(MTimers[i].State == TIMER_START)return;
            StartTimer2();
            break;
        }
        case 3: {
            for(int i=3*TIMER_PER_CHANNEL;i<4*TIMER_PER_CHANNEL;i++)
                if(MTimers[i].State == TIMER_START)return;
            StartTimer3();
            break;
        }
        default :break;
    }
    return;
}

void initialize();
void start0();
void stop0();
void start1();
void stop1();
void start2();
void stop2();
void start3();
void stop3();

ECode CMultiTimers::InitTimer()
{//move to another file
    //initialize();
    //UInt32 ticks = DzMillisecondsToTicks(1000);
    m_pMultiChannel1 = new DzTimer(100, ChannelRoutine1, NULL);

    if (!m_pMultiChannel1) {
        kprintf("ERROR: Not Enough Memory! <CMultiTimers>\n");
    }
	return  NOERROR;
}

ECode CMultiTimers::StopTimer0()
{//move to another file
    //stop0();
	return  NOERROR;
}

void CMultiTimers::StopTimer1()
{//move to another file
    m_pMultiChannel1->Cancel();
}

void CMultiTimers::StopTimer2()
{//move to another file
    //stop2();
}

void CMultiTimers::StopTimer3()
{//move to another file
    //stop3();
}

ECode CMultiTimers::StartTimer0()
{//move to another file
    //start0();
	return NOERROR;
}

void CMultiTimers::StartTimer1()
{//move to another file
    m_pMultiChannel1->Restart(100);
}

void CMultiTimers::StartTimer2()
{//move to another file
   // start2();
}

void CMultiTimers::StartTimer3()
{//move to another file
   // start3();
}

int GetTimestamp()
{
//    unsigned long * ossr=(unsigned long *)0x80a00010;;
//    return (int)*ossr;
    return 0;
}

void QueueProc(int eType, int nCode, void *pParam)
{
    unsigned long SaveValue[2]={(unsigned long)nCode,(unsigned long)pParam};
//    cprintf("the data in %d %d\n",SaveValue[0],SaveValue[1]);
    EnQueueProc(eType,0,(void *)SaveValue);
}

///////////////////////////////////////////////////////////
static CMultiTimers g_MultiTimers;

void ChannelRoutine1 (void *pvParameter)
{
    NotifyByIsr(EVENT_TIMER1,0,NULL);
    g_MultiTimers.m_pMultiChannel1->Restart(100);
}

static void OnTimerProc0(int eType, int nCode, void *pParam)
{
    g_MultiTimers.OnTimer0(nCode, pParam);
}

static void OnTimerProc1(int eType, int nCode, void *pParam)
{
    g_MultiTimers.OnTimer1(nCode, pParam);
}

static void OnTimerProc2(int eType, int nCode, void *pParam)
{
    g_MultiTimers.OnTimer2(nCode, pParam);
}

static void OnTimerProc3(int eType, int nCode, void *pParam)
{
    g_MultiTimers.OnTimer3(nCode, pParam);
}

EXTERN IDeviceDriver * CDECL CreateMultiTimer(uint_t uDeviceNo, void *pvParameter)
{
    g_MultiTimers.InitTimer();
    AddNotify(EVENT_TIMER0, (void*)OnTimerProc0);
    AddNotify(EVENT_TIMER1, (void*)OnTimerProc1);
    AddNotify(EVENT_TIMER2, (void*)OnTimerProc2);
    AddNotify(EVENT_TIMER3, (void*)OnTimerProc3);
    kprintf("Add MTimer OK\n");
    return (IDeviceDriver *)&g_MultiTimers;
}

